#include <stdlib.h>

#include "etat_partie.h"

#include "constantes_et_macros.h"





/**
 * \fn static void init_stats(struct stats* statistiques)
 * \brief Affecte a un ensemble de statistiques les valeurs de
 *        debut de partie
 *
 * \param[in] statistiques L'adresse de la structure stats a
 *                         remplir
 */
static void init_stats(struct stats* statistiques)
{
    statistiques->pv_max = VALEUR_DEPART_PV;
    statistiques->puissance = VALEUR_DEPART_STATS;
    statistiques->vitesse_charge = VALEUR_DEPART_STATS;
    statistiques->vitesse_deplacement = VALEUR_DEPART_STATS;
}



struct etat_partie* creer_partie(void)
{
    struct etat_partie* partie;

    if(!(partie = (struct etat_partie*)malloc(sizeof(struct etat_partie))))
        exit(EXIT_MALLOC);
    return partie;
}



void init_partie(struct etat_partie* partie)
{
#if defined STUFF
    int i;
#endif

    partie->nom[0] = '\0';
    partie->experience = 0;
    partie->argent = 0;
    /*
     * rappel : il n'y a pas de region 0. Un numero de region a 0
     * designe la carte du monde. De meme, un numero de zone a 0
     * veut dire que le joueur vient d'entrer dans la region et n'est
     * officiallement dans aucune de ses zones
     */
    partie->region = 0;
    partie->zone = 0;
    partie->arme = ID_ARME_DE_BASE;
    partie->veste = VIDE;
    partie->gants = VIDE;
    partie->chaussures = VIDE;
    partie->accessoire = VIDE;
    partie->objets_utilisables[0] = VIDE;
    partie->objets_utilisables[1] = VIDE;
    partie->objets_utilisables[2] = VIDE;
    init_zones_terminees(&partie->terminees, 0);
    init_regions_terminees(&partie->reussies, 0);
    init_stats(&partie->statistiques);
    ajouter_exemplaires(&partie->objets_possedes, ID_ARME_DE_BASE, 0);
#if defined STUFF
    for(i = 0;  i < 19;  i++)
        ajouter_exemplaires(&partie->objets_possedes, i, 999);
#endif
}



void detruire_partie(struct etat_partie* partie)
{
    detruire_zones_terminees(&partie->terminees);
    detruire_regions_terminees(&partie->reussies);
    detruire_inventaire(&partie->objets_possedes);
    free(partie);
}



void init_zones_terminees(struct zones_terminees* zones, const unsigned int n)
{
    if(n > 0)
    {
        if(!(zones->tab = (struct region_dans_zones_terminees*)
                malloc(n * sizeof(struct region_dans_zones_terminees))))
            exit(EXIT_MALLOC);
    }
    else
        zones->tab = NULL;
    zones->nb_elem = 0;
    zones->taille = n;
}



void marquer_zone_terminee(struct zones_terminees* zones,
                           const unsigned int id_region, const unsigned int id_zone)
{
    int i;
    struct region_dans_zones_terminees* p_region;
    struct region_dans_zones_terminees* temp;
    unsigned char* temp_tab_id_zones;

    p_region = NULL;
    i = 0;
    /*
     * on va voir si la region contenant la zone terminee est deja
     * referencee ou non. p_region vaudra NULL tant que l'on n'aura rien trouve
     */
    while(!p_region && i < zones->nb_elem)
    {
        if(zones->tab[i].id_region == id_region)
            p_region = zones->tab + i;
        else
            i++;
    }
    if(!p_region)
    {
        /* region non-trouvee. Il faut l'ajouter avant d'y ajouter la zone */
        if(zones->nb_elem == zones->taille)
        {
            /* taille insuffisante, il faut agrandir le tableau */
            zones->taille += 3;
            if(!(temp = (struct region_dans_zones_terminees*)
                    malloc(zones->taille * sizeof(struct region_dans_zones_terminees))))
                exit(EXIT_MALLOC);
            /* on recopie les anciennes valeurs dans le nouveau tableau */
            for(i = 0;  i < zones->nb_elem;  i++)
            {
                temp[i].id_region = zones->tab[i].id_region;
                temp[i].nb_elem = zones->tab[i].nb_elem;
                temp[i].taille = zones->tab[i].taille;
                temp[i].tab = zones->tab[i].tab;
            }
            /* on libere l'ancien tableau */
            if(zones->taille > 0)
                free(zones->tab);
            /* le tableau "officiel devient le nouveau */
            zones->tab = temp;
        }
        /* initialisation de la nouvelle region_dans_zones_terminees */
        p_region = zones->tab + zones->nb_elem;
        p_region->id_region = id_region;
        if(!(p_region->tab = (unsigned char*)
                malloc(3 * sizeof(unsigned char))))
            exit(EXIT_MALLOC);
        p_region->nb_elem = 0;
        p_region->taille = 3;
        zones->nb_elem++;
    }
    /*
     * ici, quoi qu'il arrive, p_region pointe vers la
     * structure region_dans_zones_terminees qui doit recevoir
     * la zone que l'on veut ajouter
     */
    if(p_region->nb_elem == p_region->taille)
    {
        /* il faut agrandir le tableau d'id de zones de la region ! */
        p_region->taille += 3;
        if(!(temp_tab_id_zones = (unsigned char*)
                malloc(p_region->taille * sizeof(unsigned char))))
            exit(EXIT_MALLOC);
        for(i = 0;  i < p_region->nb_elem;  i++)
            temp_tab_id_zones[i] = p_region->tab[i];
        /*
         * les region_dans_zones_terminees sont initialisees avec
         * une taille de 3. Le free est donc toujours possible
         */
        free(p_region->tab);
        p_region->tab = temp_tab_id_zones;
    }
    p_region->tab[p_region->nb_elem] = id_zone;
    p_region->nb_elem++;
}



void detruire_zones_terminees(struct zones_terminees* zones)
{
    int i;

    for(i = 0;  i < zones->nb_elem;  i++)
        free(zones->tab[i].tab);
    if(zones->taille > 0)
        free(zones->tab);
}




void init_regions_terminees(struct regions_terminees* regions, const unsigned int n)
{
    init_tab_dyn_id(&regions->regions, n);
    regions->taille = n;
}



void marquer_region_terminee(struct regions_terminees* regions, const unsigned int id_region)
{
    int i;
    unsigned int* nouveau_tab;

    /*
     * Si la taille est insuffisante pour accueillir
     * un nouvel element, il faut agrandir le tableau
     * et recopier les anciennes valeurs
     */
    if(regions->taille == regions->regions.nb_elements)
    {
        regions->taille++;
        if(!(nouveau_tab = (unsigned int*)malloc(regions->taille * sizeof(unsigned int))))
            exit(EXIT_MALLOC);
        for(i = 0;  i < regions->taille - 1;  i++)
            nouveau_tab[i] = regions->regions.tab[i];
        /*
         * Il faut verifier que le tableau n'avait pas une taille nulle, car si
         * c'etait le cas, aucun malloc n'avait ete fait
         */
        if(regions->taille > 1)
            free(regions->regions.tab);
        regions->regions.tab = nouveau_tab;
    }
    ajouter_id(&regions->regions, id_region);
}



char voir_si_zone_est_terminee(const struct etat_partie* partie,
                               const unsigned int id_region,
                               const unsigned int id_zone)
{
    int i;
    struct region_dans_zones_terminees* p_region;

    p_region = NULL;
    for(i = 0;  i < partie->terminees.nb_elem;  i++)
    {
        if(partie->terminees.tab[i].id_region == id_region)
            p_region = partie->terminees.tab + i;
    }
    if(!p_region)
        return FAUX;
    for(i = 0;  i < p_region->nb_elem;  i++)
    {
        if(p_region->tab[i] == id_zone)
            return VRAI;
    }
    return FAUX;
}



void detruire_regions_terminees(struct regions_terminees* regions)
{
    detruire_tab_dyn_id(&regions->regions);
    detruire_tab_dyn_id(&regions->regions);
}



unsigned int get_region_partie(const struct etat_partie* partie)
{
    return partie->region;
}



unsigned char get_zone_partie(const struct etat_partie* partie)
{
    return partie->zone;
}



unsigned int get_quantite_possedee(const struct etat_partie* partie,
                                   const unsigned int id_objet)
{
    struct objet* p_objet;

    p_objet = partie->objets_possedes.prem;
    while(p_objet)
    {
        /*
         * rappel : les objets sont classes dans l'inventaire
         * par ordre d'id croissant, on peut donc arreter la
         * recherche lorsque les id sont trop grands
         */
        if(p_objet->id == id_objet)
            return p_objet->quantite;
        else
            if(p_objet->id > id_objet)
                return 0;
        p_objet = p_objet->suiv;
    }

    return 0;
}



char voir_si_region_accessible(const struct etat_partie* partie,
                               const unsigned int id_region,
                               const struct base_donnees* donnees)
{
    int i, j;
    const struct tab_dependances* dependances;

    /*
     * une region est accessible si et seulement si :
     * - elle ne se trouve pas dans les dependances (cela signifie qu'il n'y
     *   a pas de prerequis pour y acceder)
     * - elle s'y trouve et le joueur a fini toutes les regions qu'il
     *   faut avoir finies pour y acceder
     */
    dependances = &donnees->dependances;
    for(i = 0;  i < dependances->nb_elements;  i++)
    {
        if(dependances->tab[i].id_region == id_region)
        {
            for(j = 0;  j < dependances->tab[i].tab_id.nb_elements;  j++)
            {
                if(!voir_si_region_terminee(partie,
                    dependances->tab[i].tab_id.tab[j]))
                {
                    /* cas "au moins une condition non-respectee" */
                    return FAUX;
                }
            }
            /* cas "toutes conditions respectees" */
            return VRAI;
        }
    }
    /* cas "region sans prerequis" */
    return VRAI;
}



char voir_si_region_terminee(const struct etat_partie* partie,
                             const unsigned int id_region)
{
    int i;

    for(i = 0;  i < partie->reussies.regions.nb_elements;  i++)
        if(partie->reussies.regions.tab[i] == id_region)
            return VRAI;
    return FAUX;
}


/* ============ accesseurs pour les statistiques permanentes =============== */

unsigned int get_pv_max_perm(const struct etat_partie* partie)
{
    return partie->statistiques.pv_max;
}



unsigned int get_puissance_perm(const struct etat_partie* partie)
{
    return partie->statistiques.puissance;
}



unsigned int get_vitesse_deplacement_perm(const struct etat_partie* partie)
{
    return partie->statistiques.vitesse_deplacement;
}



unsigned int get_vitesse_charge_perm(const struct etat_partie* partie)
{
    return partie->statistiques.vitesse_charge;
}



/* ============ accesseurs pour l'equipement =============== */

int get_equipement(const struct etat_partie* partie,
                   const char champ)
{
    switch(champ)
    {
        case ARME:
            return partie->arme;
            break;
        case VESTE:
            return partie->veste;
            break;
        case GANT:
            return partie->gants;
            break;
        case CHAUSSURE:
            return partie->chaussures;
            break;
        case ACCESSOIRE:
            return partie->accessoire;
            break;
        case UTIL_1:
            return partie->objets_utilisables[0];
            break;
        case UTIL_2:
            return partie->objets_utilisables[1];
            break;
        case UTIL_3:
            return partie->objets_utilisables[2];
            break;
        default:
            printf("Erreur 1 dans get_equipement.\n");
            printf("champ = %d\n", champ);
            exit(EXIT_FAILURE);
            break;
    }
}
